//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Platforms/Android/APlatform.h"
#include <stl_ext/Math/ColorFormats.h>

#if defined( PLATFORM_WINDOWS ) and defined( ANDROID_EMULATOR )

namespace Engine
{
namespace Platforms
{

	//
	// Hardware Camera Emulator
	//

	class HardwareCameraEmulator
	{
	// types
	public:
		typedef HardwareCamera::ImageFormat		ImageFormat;
		typedef AndroidHwCamera::ECommand		ECommand;
		typedef Ptr< AndroidHwCamera >			ACameraPtr;

		struct SinusGenerator;


	// variables
	private:
		binary_array_t		_preview;
		ivec2				_previewSize;
		ImageFormat::type	_previewFormat;

		bool				_enabled;
		bool				_opened;
		bool				_previewStarted;


	// methods
	public:
		HardwareCameraEmulator ();

		void Enable (bool enable);

		void Command (int cmd, float arg0, float arg1, float arg2);

		void Update ();


	// Utils
	private:
		void _StartPreview ();
		bool _LoadFromFile ();

		// generators
		template <typename GeneratorType>
		static void _GeneratePreviewRGB565 (binary_array_t &data, ImageFormat::type &fmt, const ivec2 &size, GeneratorType &gen);
		
		template <typename GeneratorType>
		static void _GeneratePreviewNV21 (binary_array_t &data, ImageFormat::type &fmt, const ivec2 &size, GeneratorType &gen);

		static ACameraPtr	_GetCamera ()	{ return AndroidPlatform::GetInstance()->GetCamera().To< ACameraPtr >(); }


	// Commands
	private:
		void _Com_Open (int index);
		void _Com_Close (bool saveState);

	};

	
#	define APLATFORM()		AndroidPlatform::GetInstance()

	
/*
=================================================
	constructor
=================================================
*/
	HardwareCameraEmulator::HardwareCameraEmulator () :
		_enabled(false), _previewStarted(false), _opened(false),
		_previewSize(480,320), _previewFormat( ImageFormat::RGB_565 )
	{
	}
	
/*
=================================================
	Enable
=================================================
*/
	void HardwareCameraEmulator::Enable (bool enable)
	{
		_enabled = enable;
	}
	
/*
=================================================
	Command
=================================================
*/
	void HardwareCameraEmulator::Command (int iCmd, float arg0, float arg1, float arg2)
	{
		const ECommand::type	cmd = ECommand::type(iCmd);

		switch ( cmd )
		{
			case ECommand::OPEN :	_Com_Open( int(arg0) );			return;
			case ECommand::CLOSE :	_Com_Close( int(arg0) != 0 );	return;
		}
	}
	
/*
=================================================
	Update
=================================================
*/
	void HardwareCameraEmulator::Update ()
	{
		if ( not _enabled or not _opened )
			return;

		if ( not _previewStarted or _preview.Empty() ) {
			_StartPreview();
			return;
		}

		_GetCamera()->OnPreview( _preview, _previewSize, _previewFormat );
	}


	
//------------------ Generators -----------------//
/*
=================================================
	SinusGenerator
=================================================
*/
	struct HardwareCameraEmulator::SinusGenerator
	{
		typedef ColorFormat::RGB32f		color_t;

		void Init (const ivec2 &size, HardwareCameraEmulator::ImageFormat::type fmt)
		{
		}

		color_t operator () (int x, int y)
		{
			color_t	c(	Sin( float(x) ), Cos( float(y) ),
						Sin( float(x+y) ) );
			return c;	
		}
	};
	


//-------------------- Utils ------------------//
/*
=================================================
	_StartPreview
=================================================
*/
	void HardwareCameraEmulator::_StartPreview ()
	{
		_previewStarted = true;

		bool loaded = _LoadFromFile();
		/*
		if ( not loaded )
		{
			SinusGenerator	gen;
			_GeneratePreviewNV21( _preview, _previewFormat, _previewSize, gen );
		}*/
	}
	
/*
=================================================
	_LoadFromFile
=================================================
*/
	bool HardwareCameraEmulator::_LoadFromFile ()
	{
		string	fname = "raw/preview_nv21_640_480.bin";

		CHECK_ERR( FileSystem::IsFileExist( fname ) );
		
		_previewSize = ivec2( 640, 480 );

		HddRFilePtr	file = File::HddRFile::New();

		CHECK_ERR( file->Open( fname ) );

		const usize	size = file->Size();
		CHECK_ERR( size == _previewSize.x * _previewSize.y * 3/2 );

		_preview.Resize( size, false );
		CHECK_ERR( file->Read( _preview.ptr(), _preview.Size() ) );
		
		return true;
	}

/*
=================================================
	_GeneratePreviewRGB565
=================================================
*/
	template <typename GeneratorType>
	void HardwareCameraEmulator::_GeneratePreviewRGB565 (binary_array_t &data, ImageFormat::type &fmt,
														 const ivec2 &size, GeneratorType &gen)
	{
		ASSERT( size.x == Align<4>( size.x ) );
		ASSERT( size.y == Align<4>( size.y ) );

		const uint	bit_per_pixel = 5+6+5;

		fmt = ImageFormat::RGB_565;
		data.Resize( (usize) Ceil( (size.x * size.y * bit_per_pixel) / 8.0 ) );

		ColorFormat::R5_G6_B5_UNorm *	pixels = (ColorFormat::R5_G6_B5_UNorm *) data.ptr();
		
		gen.Init( size, fmt );

		for (int y = 0; y < size.y; ++y)
		{
			for (int x = 0; x < size.x; ++x)
			{
				const int i = x + y * size.x;

				ColorFormat::ColorFormatConverter::Convert( pixels[i], gen( x, y ) );
			}
		}
	}

/*
=================================================
	_GeneratePreviewNV21
=================================================
*/
	template <typename GeneratorType>
	void HardwareCameraEmulator::_GeneratePreviewNV21 (binary_array_t &data, ImageFormat::type &fmt,
														const ivec2 &size, GeneratorType &gen)
	{
		ASSERT( size.x == Align<4>( size.x ) );
		ASSERT( size.y == Align<4>( size.y ) );

		const uint	bit_per_pixel = 8+4;

		fmt = ImageFormat::NV21;
		data.Resize( (usize) Ceil( (size.x * size.y * bit_per_pixel) / 8.0 ) );

		ColorFormat::R8_UNorm *	luminance = (ColorFormat::R8_UNorm *) data.ptr();

		gen.Init( size, fmt );

		for (int y = 0; y < size.y; ++y)
		{
			for (int x = 0; x < size.x; ++x)
			{
				const int i = x + y * size.x;

				ColorFormat::ColorFormatConverter::Convert( luminance[i], gen( x, y ) );
			}
		}
	}



//------------------ Commands -----------------//
/*
=================================================
	_Com_Open
=================================================
*/
	void HardwareCameraEmulator::_Com_Open (int index)
	{
		_opened = true;
	}
	
/*
=================================================
	_Com_Close
=================================================
*/
	void HardwareCameraEmulator::_Com_Close (bool saveState)
	{
		_opened			= false;
		_previewStarted	= false;
	}


#	undef APLATFORM

}	// Platforms
}	// Engine

#endif	// PLATFORM_WINDOWS and ANDROID_EMULATOR